

package hotgammon.common;

import hotgammon.alphamon.AlphaMonFactory;

import java.util.*;

public class GameImpl implements Game {
	private Color playerInTurn; /* The color of the player currently in turn */
	private Map<Location, Color> locationColors; /* Map from Locations to colors */
	private Map<Location, Integer> locationCount; /* Map from Locations to checker count */
	private int turn = 0; /* The current turn */
	private int movesLeft; /* Moves left in the current turn */
	private ValidatorStrategy validatorStrategy;
	private int[] currentDiceThrow; // The dice thrown in this turn. If a dice is used it is set to 0

	private int[][] rolls = {{5,6}, {1,2}, {3,4}}; /* The dice-rolls available in alpha-mon */
	Random random = new Random();

	private WinnerStrategy winnerStrategy;
	private SetupPiecesStrategy setupPiecesStrategy;
	private DiceThrowStrategy diceThrowStrategy;

	private HotGammonVariant hotGammonVariant;

	//public GameImpl(HotgammonFactory hotgammonFactory, HotGammonVariant hotGammonVariant) {
	public GameImpl(HotGammonVariant hotGammonVariant) {
		HotgammonFactory hotgammonFactory = new AlphaMonFactory();
		currentDiceThrow = new int[2];
		this.validatorStrategy = hotgammonFactory.createValidatorStrategy();
		this.winnerStrategy = hotgammonFactory.createWinnerStrategy();
		this.setupPiecesStrategy = hotgammonFactory.createSetupPiecesStrategy();
		this.diceThrowStrategy = hotgammonFactory.createDiceThroStrategy();

		this.hotGammonVariant = hotGammonVariant;

		//		if (this.hotGammonVariant == HotGammonVariant.BETAMON) {
		//			System.out.println("hest");
		//		}
	}

	/** Reset the entire game to start from scratch. 
	 * No player is in turn, and the game awaits a
	 * call to nextTurn to start a game.
	 */
	public void newGame() {
		playerInTurn = Color.NONE;
		locationColors = new HashMap<Location, Color>();
		locationCount = new HashMap<Location, Integer>();

		setupPiecesStrategy.setupPieces(locationColors, locationCount);

	}

	public void nextTurn() {
		turn++; // Increment the turn count 
		movesLeft = 2; // reset moves left
		if (playerInTurn == Color.BLACK) {
			playerInTurn = Color.RED;
		} else {
			playerInTurn = Color.BLACK;
		}

		switch (hotGammonVariant) {
		case EPSILONMON:  
			currentDiceThrow = diceThrowEpsilonmon();
			break;
		default: 
			currentDiceThrow = rolls[turn%3];
			break;
		}
	}

	public boolean move(Location from, Location to) {
		if (movesLeft == 0) return false; // No move is allowed
		if (getColor(from) != playerInTurn ) return false; //Only move a friendly checker

		boolean movePerformed = false;
		if (validatorStrategy.isValid(from, to, currentDiceThrow[0], this) && currentDiceThrow[0] != 0) {
			updateBoard(from,to,playerInTurn,currentDiceThrow[0]);            
			currentDiceThrow[0] = 0; //Zero the dice value to indicate it has been used
			movePerformed = true;
		}
		else if (validatorStrategy.isValid(from, to, currentDiceThrow[1], this)) {
			updateBoard(from,to,playerInTurn,currentDiceThrow[1]);            
			currentDiceThrow[1] = 0;
			movePerformed = true;
		}

		if (movePerformed) {
			movesLeft--;
			return true;
		}

		return false;
	}

	private void updateBoard(Location from, Location to, Color playerInTurn2, int diceValue) {

		if (locationColors.get(to) != playerInTurn) //Move is not to a friendly field 
		{
			if (locationColors.get(to) !=  Color.NONE) //Conquering opponent field so move opponent to bar
				if (playerInTurn == Color.BLACK) 
					locationCount.put(Location.R_BAR, locationCount.get(Location.R_BAR) +1 ); 
				else {
					locationCount.put(Location.B_BAR, locationCount.get(Location.B_BAR) + 1);
				}
			locationColors.put(to, playerInTurn);
		}


		locationCount.put(from, locationCount.get(from) - 1); // Decrement the count of checker in the from location
		if (getCount(from) == 0) { // If the checker count in from is 0 remove its color.
			locationColors.put(from, Color.NONE);
		}
		locationCount.put(to, locationCount.get(to) + 1); // Increment the count of the to location
	}

	public Color getPlayerInTurn() {
		return playerInTurn;
	}

	public int getNumberOfMovesLeft() { 
		return movesLeft; 
	}

	public int[] diceThrown() { 

		return currentDiceThrow; 
	}

	public int[] diceValuesLeft() { 
		if (movesLeft == 2) return currentDiceThrow;
		if (movesLeft == 1) {
			if (currentDiceThrow[0] == 0)
				return new int[]{currentDiceThrow[1]};
			else
				return new int[]{currentDiceThrow[0]};
		}
		return new int []{}; 
	}

	public Color winner() {
		if (hotGammonVariant == HotGammonVariant.GAMMAMON) {
			if (locationCount.containsKey(Location.R_BEAR_OFF)) {
				if (locationCount.get(Location.R_BEAR_OFF) >= 15 ) {
					return Color.RED;
				}
			}

			if (locationCount.containsKey(Location.B_BEAR_OFF)) {
				if (locationCount.get(Location.B_BEAR_OFF) >= 15 ) {
					return Color.BLACK;
				}
			}

			return Color.NONE; 
		} else {
			if (turn == 6) return Color.RED;
			return Color.NONE;
		}
		//return winnerStrategy.winner(turn, locationCount);
	}

	public Color getColor(Location location) { 
		if (!locationColors.containsKey(location)) return Color.NONE;
		return this.locationColors.get(location);
	}

	public int getCount(Location location) {
		if (this.locationCount.containsKey(location)) {
			return this.locationCount.get(location);   
		} else {
			return 0;
		}
	}

	private int[] diceThrowAlphamon(int turn) {
		return this.rolls[turn%3];
	}


	private int[] diceThrowEpsilonmon() {
		int[] currentDiceThrow; 
		currentDiceThrow = new int[2];

		for (int i = 0; i <= 1; i++) {
			currentDiceThrow[i] = generateRandomNumberBetween1And6(); 
		}

		return currentDiceThrow;
	}

	private int generateRandomNumberBetween1And6() {
		//nextInt(5) generates numbers between 0 (incl) and 6 (excl), 
		//by adding 1 we change interval to numbers between 1 (incl) and 7 (excl)
		//which is numbers between 1 and 6 both incl.
		return (this.random.nextInt(6) + 1 );
	}
}
